package com.jy.rhin.config.ws;


import cn.hutool.core.util.XmlUtil;
import com.jy.rhin.domain.document.node.NodeAuthenticationPermissionService;
import com.jy.rhin.support.util.CertificateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.util.List;
@Component
@Slf4j
/**
 * 服务端安全证书和签名的检测
 * @author TJT
 *
 */
public class ServerAuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
	@Autowired
	private NodeAuthenticationPermissionService nodeAuthenticationPermissionService;
	
	public ServerAuthInterceptor() {
		super(Phase.PRE_INVOKE);
	}

	@Override
	public void handleMessage(SoapMessage message) throws Fault {
		try {
            List<Header> headers = message.getHeaders();
            if (ObjectUtils.isEmpty(headers)) {
                throw new RuntimeException("缺失证书");
            }
            Element header =  (Element)headers.get(0).getObject();
            StringBuffer cerbuf = new StringBuffer();
            cerbuf.append("-----BEGIN CERTIFICATE-----\n");
            String  cer = header.getElementsByTagName("wsse:BinarySecurityToken").item(0).getTextContent();
            cerbuf.append(cer.trim());
            cerbuf.append("\n-----END CERTIFICATE-----");
            log.info("读取证书\n{}",cerbuf.toString());
            //证书签名认证
            String timestamp = header.getElementsByTagName("wsu:Timestamp").item(0).getTextContent();
            String  digest = CertificateUtils.digest(timestamp);
            digest += CertificateUtils.digest(cer);
            CachedOutputStream out= message.getContent(CachedOutputStream.class);
            Document d = XmlUtil.readXML(out.getInputStream());
            String body = null;
            NodeList bodyEle =  d.getElementsByTagName("soapenv:Body");
            //兼容不同标准
            if(bodyEle == null || bodyEle.item(0)== null){
            	body = d.getElementsByTagName("soap:Body").item(0).getTextContent().trim();
            }else{
            	body = bodyEle.item(0).getTextContent().trim();
            }
            digest += CertificateUtils.digest(body);
            String sign = ((Element)header.getElementsByTagName("ds:Signature").item(0)).
            		getElementsByTagName("ds:SignatureValue").item(0).getTextContent();
            if(!CertificateUtils.verify(cerbuf.toString(), digest, sign)){
            	throw new RuntimeException("验签失败");
            }
            //权限认证
            String publicKey = CertificateUtils.getPublicKey(cerbuf.toString());
            String action = message.getOrDefault("SOAPAction", null).toString();
            if(!nodeAuthenticationPermissionService.hasPermission(publicKey, action)){
            	throw new RuntimeException("证书没有权限,请授权后重试");
            }
        } catch (RuntimeException e) {
            throw e;
        }catch(Exception e){
        	throw new RuntimeException("证书认证失败");
        }

	}
	
}
